/*
 * Ingenic IMP SDK emulator test.
 *
 * Copyright (C) 2014 Ingenic Semiconductor Co.,Ltd
 * Author: Zoro <ykli@ingenic.cn>
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>

#include <CUnit/CUnit.h>
#include <CUnit/Automated.h>
#include <CUnit/Basic.h>
#include <CUnit/Console.h>

#include <unistd.h>
#include <sys/time.h>

#include <imp/imp_log.h>
#include <imp/imp_common.h>
#include <imp/imp_system.h>
#include <imp/imp_framesource.h>
#include <imp/imp_isp.h>
#include <imp/imp_encoder.h>

#include <system/system.h>
#include <system/vbm.h>

#define TAG "Framesource"

#define SENSOR_FRAME_RATE		25
#define MAX_FRAMES_TO_SAVE		200
#define FILE_NAME_LEN	50
#define POLLING_TIMEOUT			(50000)
#define DEPTH_FR			2
#define DEPTH_DS1			2
//#define SAVE_STREAM

typedef void *(*GetStreamFunc)(void *);
static int IMPSetFrameDepth(void );

#ifdef SENSOR_OV5645
static IMPSensorInfo sensor_info = {
	.name = "ov5645",
	.cbus_type = TX_SENSOR_CONTROL_INTERFACE_I2C,
	.i2c = {
		.type = "ov5645",
		.addr = 0x3c,
		.i2c_adapter_id = 3,
	},
	.rst_gpio = 0,
	.pwdn_gpio = 0,
	.power_gpio = 0,
};
#elif defined(SENSOR_AR0141)
static IMPSensorInfo sensor_info = {
	.name = "ar0141",
	.cbus_type = TX_SENSOR_CONTROL_INTERFACE_I2C,
	.i2c = {
		.type = "ar0141",
		.addr = 0x10,
		.i2c_adapter_id = 0,
	},
	.rst_gpio = 0,
	.pwdn_gpio = 0,
	.power_gpio = 0,
};
#else
static IMPSensorInfo sensor_info = {
	.name = "ov9712",
	.cbus_type = TX_SENSOR_CONTROL_INTERFACE_I2C,
	.i2c = {
		.type = "ov9712",
		.addr = 0x30,
		.i2c_adapter_id = 0,
	},
	.rst_gpio = 0,
	.pwdn_gpio = 0,
	.power_gpio = 0,
};
#endif

static int ImpSystemInit()
{
	int ret = 0;

	IMP_LOG_DBG(TAG, "ImpSystemInit start\n");

	if(IMP_ISP_Open()){
		IMP_LOG_ERR(TAG, "failed to EmuISPOpen\n");
		return -1;
	}

	ret = IMP_ISP_AddSensor(&sensor_info);
	if(ret < 0){
		IMP_LOG_ERR(TAG, "failed to AddSensor\n");
		return -1;
	}

	ret = IMP_ISP_EnableSensor();
	if(ret < 0){
		IMP_LOG_ERR(TAG, "failed to EnableSensor\n");
		return -1;
	}

	IMP_System_Init();

	IMP_LOG_DBG(TAG, "ImpSystemInit success\n");

	return 0;
}

static int ImpSystemExit()
{
	int ret = 0;

	IMP_LOG_DBG(TAG, "ImpSystemExit start\n");

	IMP_System_Exit();

	ret = IMP_ISP_DisableSensor();
	if(ret < 0){
		IMP_LOG_ERR(TAG, "failed to DisableSensor\n");
		return -1;
	}

	ret = IMP_ISP_DelSensor(&sensor_info);
	if(ret < 0){
		IMP_LOG_ERR(TAG, "failed to DelSensor\n");
		return -1;
	}

	IMP_ISP_Close();

	IMP_LOG_DBG(TAG, "ImpSystemExit success\n");

	return 0;
}

#ifdef SAVE_STREAM
static int save_stream(int fd, int chnNum, IMPEncoderStream *stream)
{
	int ret, i, nr_pack = stream->packCount;

	for (i = 0; i < nr_pack; i++) {
		ret = write(fd, (void *)stream->pack[i].virAddr,
					stream->pack[i].length);
		if (ret != stream->pack[i].length) {
			IMP_LOG_ERR(TAG, "channel%d stream write error:%s\n", chnNum, strerror(errno));
			return -1;
		}
	}

	return 0;
}
#endif

static int64_t cur_usec(void)
{
    struct timeval tv_date;
    gettimeofday(&tv_date, NULL);

    return (int64_t)tv_date.tv_sec * 1000000 + (int64_t)tv_date.tv_usec;
}

static int64_t print_status(int64_t i_start, int64_t i_previous, int i_frame, int i_frame_total)
{
	char buf[200];
	int64_t i_time = cur_usec();

	if(i_previous && i_time - i_previous < 250000 )
		return i_previous;

	int64_t i_elapsed = i_time - i_start;
	double fps = i_elapsed > 0 ? i_frame * 1000000. / i_elapsed : 0;
	if (i_frame_total) {
		sprintf(buf, TAG "[%.1f%%] %d/%d frames, %.2lf fps",
				100. * i_frame / i_frame_total, i_frame, i_frame_total, fps);
	} else {
		sprintf(buf, TAG " %d frames: %.2lf fps", i_frame, fps);
	}

	fprintf(stderr, "%s\r", buf);

	return i_time;
}

static int ImpFrameSourceInit()
{
	int ret;

	/* Configure channel 0 attr */
	IMPFSChnAttr imp_chn_attr;
	imp_chn_attr.pixFmt = PIX_FMT_NV12;
	imp_chn_attr.outFrmRate = SENSOR_FRAME_RATE;
	imp_chn_attr.nrVBs = 1;

	imp_chn_attr.crop.enable = 1;
	imp_chn_attr.crop.top = 0;
	imp_chn_attr.crop.left = 0;
	imp_chn_attr.crop.width = 1280;
	imp_chn_attr.crop.height = 720;

	imp_chn_attr.scaler.enable = 0;

	imp_chn_attr.picWidth = 1280;
	imp_chn_attr.picHeight = 720;

	/* create channel 0*/
	ret = IMP_FrameSource_CreateChn(0, &imp_chn_attr);
	if(ret < 0){
		IMP_LOG_ERR(TAG, "IMP_FrameSource_CreateChn(chn0) error!\n");
		return -1;
	}

	/* Check channel 0 attr */
	IMPFSChnAttr imp_chn_attr_check;
	ret = IMP_FrameSource_GetChnAttr(0, &imp_chn_attr_check);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_FrameSource_GetChnAttr(0) error: %d\n", ret);
		return -1;
	}
	IMP_LOG_DBG(TAG, "FS Chn0 ChnAttr.picWidth=%d\n", imp_chn_attr_check.picWidth);
	IMP_LOG_DBG(TAG, "FS Chn0 ChnAttr.picHeight=%d\n", imp_chn_attr_check.picHeight);
	IMP_LOG_DBG(TAG, "FS Chn0 ChnAttr.pixFmt=%d\n", imp_chn_attr_check.pixFmt);
	IMP_LOG_DBG(TAG, "FS Chn0 ChnAttr.outFrmRate=%d\n", imp_chn_attr_check.outFrmRate);

	/* Configure channel 1 attr */
//	imp_chn_attr.pixFmt = PIX_FMT_YUYV422;
	imp_chn_attr.pixFmt = PIX_FMT_NV12;
	imp_chn_attr.outFrmRate = SENSOR_FRAME_RATE;
	imp_chn_attr.nrVBs = 5;

	imp_chn_attr.crop.enable = 1;
	imp_chn_attr.crop.top = 0;
	imp_chn_attr.crop.left = 0;
	imp_chn_attr.crop.width = 1280;
	imp_chn_attr.crop.height = 720;

	imp_chn_attr.scaler.enable = 1;
	imp_chn_attr.scaler.outwidth = 640;
	imp_chn_attr.scaler.outheight = 360;

	imp_chn_attr.picWidth = 640;
	imp_chn_attr.picHeight = 360;
	/* create channel 1*/
	ret = IMP_FrameSource_CreateChn(1, &imp_chn_attr);
	if(ret < 0){
		IMP_LOG_ERR(TAG, "IMP_FrameSource_CreateChn(chn1) error!\n");
		return -1;
	}

	/* Check channel 1 attr */
	ret = IMP_FrameSource_GetChnAttr(1, &imp_chn_attr_check);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_FrameSource_GetChnAttr(1) error: %d\n", ret);
		return -1;
	}
	IMP_LOG_DBG(TAG, "FS Chn1 ChnAttr.picWidth=%d\n", imp_chn_attr_check.picWidth);
	IMP_LOG_DBG(TAG, "FS Chn1 ChnAttr.picHeight=%d\n", imp_chn_attr_check.picHeight);
	IMP_LOG_DBG(TAG, "FS Chn1 ChnAttr.pixFmt=%d\n", imp_chn_attr_check.pixFmt);
	IMP_LOG_DBG(TAG, "FS Chn1 ChnAttr.outFrmRate=%d\n", imp_chn_attr_check.outFrmRate);

	IMP_LOG_DBG(TAG, "%s(): OK.\n", __func__);

	return 0;
}

static int ImpFrameSourceExit()
{
	IMP_FrameSource_DestroyChn(0);
	IMP_FrameSource_DestroyChn(1);
	return 0;
}

static int ImpStreamOn()
{
	int ret = 0;
	/* Enable channels */
	ret = IMP_FrameSource_EnableChn(0);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_FrameSource_EnableChn(0) error: %d\n", ret);
		return -1;
	}

	ret = IMP_FrameSource_EnableChn(1);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_FrameSource_EnableChn(1) error: %d\n", ret);
		return -1;
	}

	return 0;
}

static int ImpStreamOff()
{
	int ret = 0;
	/* Disable channels */
	ret = IMP_FrameSource_DisableChn(0);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_FrameSource_EnableChn(0) error: %d\n", ret);
		return -1;
	}

	ret = IMP_FrameSource_DisableChn(1);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_FrameSource_EnableChn(1) error: %d\n", ret);
		return -1;
	}
	return 0;
}

static int encoder_init_h264(void)
{
	int ret, i;

	/* Init Channel attr */
	IMPEncoderAttr enc_attr;
	memset(&enc_attr, 0, sizeof(IMPEncoderAttr));
	enc_attr.enType = PT_H264;
	enc_attr.bufSize = 0;
	enc_attr.profile = 0;

	IMPEncoderRcAttr rc_attr;
	memset(&rc_attr, 0, sizeof(IMPEncoderRcAttr));
#ifdef CONFIG_X264
	rc_attr.rcMode = ENC_RC_MODE_VBR;
	rc_attr.attrH264Vbr.maxGop = SENSOR_FRAME_RATE;
	rc_attr.attrH264Vbr.inFrmRate = SENSOR_FRAME_RATE;
	rc_attr.attrH264Vbr.outFrmRate = SENSOR_FRAME_RATE;
	rc_attr.attrH264Vbr.maxBitRate = 1800;
	rc_attr.attrH264Vbr.maxQp = 34;
	rc_attr.attrH264Vbr.minQp = 26;
#else
	rc_attr.rcMode = ENC_RC_MODE_CBR;
	rc_attr.attrH264Cbr.maxGop = SENSOR_FRAME_RATE * 3;
	rc_attr.attrH264Cbr.inFrmRate = SENSOR_FRAME_RATE;
	rc_attr.attrH264Cbr.outFrmRate = SENSOR_FRAME_RATE;
	rc_attr.attrH264Cbr.outBitRate = 300;
	rc_attr.attrH264Cbr.maxQp = 34;
	rc_attr.attrH264Cbr.minQp = 24;
	rc_attr.attrH264Cbr.iBiasLvl = 0;
	rc_attr.attrH264Cbr.frmQPStep = 7;
	rc_attr.attrH264Cbr.gopQPStep = 7;
	rc_attr.attrH264Cbr.adaptiveMode = false;
	rc_attr.attrH264Cbr.gopRelation = false;
#endif

	IMPEncoderCHNAttr channel_attr;

	for (i = 0; i < 2; i++) {
		if (i == 0) {
			enc_attr.picWidth = 1280;
			enc_attr.picHeight = 720;
		} else {
			enc_attr.picWidth = 640;
			enc_attr.picHeight = 360;
		}
		channel_attr.encAttr = enc_attr;
		channel_attr.rcAttr = rc_attr;
		/* Creat Encoder Group */
		ret = IMP_Encoder_CreateGroup(i);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_CreateGroup(%d) error: %d\n", i, ret);
			return -1;
		}

		/* Create Channel */
		ret = IMP_Encoder_CreateChn(i, &channel_attr);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_CreateChn(%d) error: %d\n", i, ret);
			return -1;
		}

		/* Resigter Channel */
		ret = IMP_Encoder_RegisterChn(i, i);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_RegisterChn(%d, %d) error: %d\n", i, i, ret);
			return -1;
		}
	}

	return 0;
}

static int encoder_exit_h264(void)
{
	int i = 0;

	for (i = 0; i < 2; i++) {
		IMP_Encoder_UnRegisterChn(i);
		IMP_Encoder_DestroyChn(i);
		IMP_Encoder_DestroyGroup(i);
	}

	IMP_LOG_DBG(TAG, "exit OK\n");

	return 0;
}

static int encoder_init_jpeg(void)
{
	int ret, i;

	/* Init Channel attr */
	IMPEncoderAttr enc_attr;
	memset(&enc_attr, 0, sizeof(IMPEncoderAttr));
	enc_attr.enType = PT_JPEG;
	enc_attr.bufSize = 0;
	enc_attr.profile = 0;

	IMPEncoderCHNAttr channel_attr;
	memset(&channel_attr, 0, sizeof(IMPEncoderCHNAttr));

	for (i = 0; i < 2; i++) {
		if (i == 0) {
			enc_attr.picWidth = 640;
			enc_attr.picHeight = 240;
		} else {
			enc_attr.picWidth = 320;
			enc_attr.picHeight = 240;
		}

		channel_attr.encAttr = enc_attr;

		/* Creat Encoder Group */
		ret = IMP_Encoder_CreateGroup(i);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_CreateGroup(%d) error: %d\n", i, ret);
			return -1;
		}

		/* Create Channel */
		ret = IMP_Encoder_CreateChn(i, &channel_attr);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_CreateChn(%d) error: %d\n", i, ret);
			return -1;
		}

		/* Resigter Channel */
		ret = IMP_Encoder_RegisterChn(i, i);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_RegisterChn(%d, %d) error: %d\n", i, i, ret);
			return -1;
		}
	}

	return 0;
}

static int encoder_exit_jpeg(void)
{
	int i = 0;

	for (i = 0; i < 2; i++) {
		IMP_Encoder_UnRegisterChn(i);
		IMP_Encoder_DestroyChn(i);
		IMP_Encoder_DestroyGroup(i);
	}

	IMP_LOG_DBG(TAG, "exit OK\n");

	return 0;
}

#ifdef SAVE_STREAM
static int open_stream_file(char *file_path)
{
	int fd = open(file_path, O_RDWR | O_CREAT | O_TRUNC, 0664);
	return fd;
}

static void close_stream_file(int fd)
{
	close(fd);
}
#endif

static void dump_enc_chn_stat(IMPEncoderCHNStat *chn_stat)
{
	IMP_LOG_DBG(TAG, "Dump Encoder Channel stat:\n");
	IMP_LOG_DBG(TAG, "\tregistered:%d\n", chn_stat->registered);
	IMP_LOG_DBG(TAG, "\tleftPics:%u\n", chn_stat->leftPics);
	IMP_LOG_DBG(TAG, "\tleftStreamBytes:%u\n", chn_stat->leftStreamBytes);
	IMP_LOG_DBG(TAG, "\tleftStreamFrames:%u\n", chn_stat->leftStreamFrames);
	IMP_LOG_DBG(TAG, "\tcurPacks:%u\n", chn_stat->curPacks);
}

static void *do_get_channel_stream(void *arg)
{
	int ret, chnNum = (int)arg;
	int64_t i_start_time = cur_usec();
	int64_t i_previous_time = 0;
	int i;

	ret = IMP_Encoder_StartRecvPic(chnNum);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_Encoder_StartRecvPic(%d) failed\n", chnNum);
		return (void *)-1;
	}

#ifdef SAVE_STREAM
	char stream_file_name[FILE_NAME_LEN];
	sprintf(stream_file_name, "/tmp/channel%d_stream.h264", chnNum);
	int stream_fd = open_stream_file(stream_file_name);
#endif

	for (i = 0; i < MAX_FRAMES_TO_SAVE; i++) {
		ret = IMP_Encoder_PollingStream(chnNum, POLLING_TIMEOUT);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "Polling channel%d stream timeout\n");
			return (void *)-1;
		}

		IMPEncoderStream stream;
		IMPEncoderCHNStat chn_stat;
		IMP_Encoder_Query(chnNum, &chn_stat);
		stream.packCount = chn_stat.curPacks;

		dump_enc_chn_stat(&chn_stat);

		ret = IMP_Encoder_GetStream(chnNum, &stream, 1);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "IMP_Encoder_GetStream(%d) failed\n", chnNum);
			return (void *)-1;
		}

		/* Dump stream */
		IMP_LOG_DBG(TAG, "chnNum=%d, stream seq=%d packCount=%d\n",
					chnNum, stream.seq, stream.packCount);

#ifdef SAVE_STREAM
		ret = save_stream(stream_fd, chnNum, &stream);
		if (ret < 0) {
			close(stream_fd);
			return (void *)ret;
		}
#endif

		IMP_Encoder_ReleaseStream(chnNum, &stream);
		i_previous_time = print_status(i_start_time, i_previous_time, i, 0);
	}

#ifdef SAVE_STREAM
	close_stream_file(stream_fd);
#endif

	ret = IMP_Encoder_StopRecvPic(chnNum);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_Encoder_StopRecvPic(%d) failed\n", chnNum);
		return (void *)-1;
	}

	return (void *)0;
}

static void *do_get_channel_snap(void *arg)
{
	int ret, chnNum = (int)arg;

	ret = IMP_Encoder_StartRecvPic(chnNum);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_Encoder_StartRecvPic(%d) failed\n", chnNum);
		return (void *)-1;
	}

	ret = IMP_Encoder_PollingStream(chnNum, POLLING_TIMEOUT);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "Polling stream timeout\n");
		return (void *)-1;
	}

	IMPEncoderStream stream;
	IMPEncoderCHNStat chn_stat;
	IMP_Encoder_Query(chnNum, &chn_stat);
	stream.packCount = chn_stat.curPacks;

	dump_enc_chn_stat(&chn_stat);

	ret = IMP_Encoder_GetStream(chnNum, &stream, 1);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_Encoder_GetStream(%d) failed\n", chnNum);
		return (void *)-1;
	}

	/* Dump stream */
	IMP_LOG_DBG(TAG, "channel=%d, stream seq=%d packCount=%d\n",
				chnNum, stream.seq, stream.packCount);

#ifdef SAVE_STREAM
	char snap_file_name[FILE_NAME_LEN];
	sprintf(snap_file_name, "/tmp/channel%d_snap.jpg", chnNum);
	int stream_fd = open_stream_file(snap_file_name);

	ret = save_stream(stream_fd, chnNum, &stream);
	if (ret < 0) {
		close_stream_file(stream_fd);
		return (void *)ret;
	}
#endif

	IMP_Encoder_ReleaseStream(chnNum, &stream);

#ifdef SAVE_STREAM
	close_stream_file(stream_fd);
#endif

	ret = IMP_Encoder_StopRecvPic(chnNum);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP_Encoder_StopRecvPic(%d) failed\n", chnNum);
		return (void *)-1;
	}

	return (void *)0;
}

static int do_get_stream(GetStreamFunc getStreamFunc[2])
{
	int ret = -1, i;
	pthread_t tid[2] = {-1, -1};

	for (i = 0; i < 2; i++) {
		if (pthread_create(&tid[i], NULL, getStreamFunc[i], (void *)i)) {
			IMP_LOG_ERR(TAG, "pthread_create get channel %d stream failed\n", i);
			goto err_pthread_create;
		}
	}

	for (i = 1; i >= 0; i--) {
		if (tid[i] >= 0) {
			if ((pthread_join(tid[i], (void **)&ret) < 0) || (ret < 0)) {
				IMP_LOG_ERR(TAG, "pthread_join channel %d stream failed\n", i, ret);
				goto err_pthread_join;
			}
			tid[i] = -1;
		}
	}

	return 0;

err_pthread_join:
err_pthread_create:
	for (i = 1; i >= 0; i--) {
		if (tid[i] >= 0) {
			pthread_cancel(tid[i]);
			pthread_join(tid[i], (void **)&ret);
		}
	}

	return -1;
}

static int ImpBindModules()
{
	IMPCell framesource_chn0 = {DEV_ID_FS, 0, 0};
	IMPCell framesource_chn1 = {DEV_ID_FS, 1, 0};
	IMPCell encoder0 = { DEV_ID_ENC, 0, 0};
	IMPCell encoder1 = { DEV_ID_ENC, 1, 0};

	IMP_System_Bind(&framesource_chn0, &encoder0);
	IMP_System_Bind(&framesource_chn1, &encoder1);

	system_bind_dump();

	/* Bind modules here. */
	return 0;
}

static int ImpUnBindModules()
{
	IMPCell framesource_chn0 = {DEV_ID_FS, 0, 0};
	IMPCell framesource_chn1 = {DEV_ID_FS, 1, 0};
	IMPCell encoder0 = { DEV_ID_ENC, 0, 0};
	IMPCell encoder1 = { DEV_ID_ENC, 1, 1};

	IMP_System_UnBind(&framesource_chn0, &encoder0);
	IMP_System_UnBind(&framesource_chn1, &encoder1);

	return 0;
}

static int IMPSetFrameDepth(void )
{
	int depth;

	IMP_FrameSource_SetFrameDepth(0, DEPTH_FR);

	IMP_FrameSource_GetFrameDepth(0, &depth);
	if(depth != DEPTH_FR) {
		printf("ERROR : the depth of fr is %d\n", depth);
		CU_FAIL();
	}

	IMP_FrameSource_SetFrameDepth(1, DEPTH_DS1);

	IMP_FrameSource_GetFrameDepth(1, &depth);
	if(depth != DEPTH_DS1) {
		printf("ERROR : the depth of ds1 is %d\n", depth);
		CU_FAIL();
	}
#if 1
	int i, ret;
	IMPFrameInfo *frame2;
	IMPFrameInfo *frame3;
	IMPFrameInfo *frame4;
	for(i = 0; i < 2; i++) {
		ret = IMP_FrameSource_GetFrame(i, &frame2);
		if(ret != 0) {
			printf("ERROR(IMP_FrameSource_GetFrame) : %d\n", __LINE__);
		}

		ret = IMP_FrameSource_GetFrame(i, &frame3);
		if(ret != 0) {
			printf("ERROR(IMP_FrameSource_GetFrame) : %d\n", __LINE__);
		}

		ret = IMP_FrameSource_GetFrame(i, &frame4);
		if(ret != 0) {
			printf("ERROR(IMP_FrameSource_GetFrame) : %d\n", __LINE__);
		}

		IMP_FrameSource_ReleaseFrame(i, frame2);
		IMP_FrameSource_ReleaseFrame(i, frame3);
	}
#endif
#if 1
	int fd, chn, times;
	char path[128];
	IMPFrameInfo *frame1;

	for(chn = 0; chn < 2; chn++) {
		for(times = 0; times < 2; times++) {
			IMP_FrameSource_GetFrame(chn, &frame1);

			sprintf(path, "frame-%d-%d", chn, times);
			fd = open(path, O_RDWR | O_CREAT, 0777);
			if(fd < 0) {
				printf("fd error !\n");
				return -1;
			}

			write(fd, (void *)frame1->virAddr, frame1->size);

			close(fd);
			printf("save picture ok!\n");
			IMP_FrameSource_ReleaseFrame(0, frame1);
		}
	}
#endif
#if 1
	int j, /*timeout, */num = 10;
	struct timeval timeval, timeval1;
	IMPFrameInfo *frame;
	gettimeofday(&timeval, NULL);

	for(j = 0; j < num; j++) {
		IMP_FrameSource_GetFrame(0, &frame);
		IMP_FrameSource_ReleaseFrame(0, frame);
	}

	gettimeofday(&timeval1, NULL);

	//printf("\ntime : %d:%d\n", timeval.tv_sec, timeval.tv_usec);
	//printf("\ntime1 : %d:%d\n", timeval1.tv_sec, timeval1.tv_usec);

	//timeout = (timeval1.tv_sec - timeval.tv_sec) * 1000000 + (timeval1.tv_usec - timeval.tv_usec);
	//printf("\n\ntimeout : %dus\n", timeout/num);
#endif
	while(1);

	return 0;
}

static void test_framesource_h264_stream() /* SYS+emuFS+ENC+h264 */
{
	int ret;

	ret = ImpSystemInit();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP system init failed\n");
		goto err_ImpSystemInit;
	}

	ret = ImpFrameSourceInit();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP ISP init failed\n");
		goto err_ImpFrameSourceInit;
	}

	ret = encoder_init_h264();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP Encoder init failed\n");
		goto err_encoder_init_h264;
	}

	ret = ImpBindModules();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP Encoder init failed\n");
		goto err_ImpBindModules;
	}

	ret = ImpStreamOn();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP stream on error\n");
		goto err_ImpStreamOn;
	}

	ret = IMPSetFrameDepth();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP stream frames depth error\n");
	}

	GetStreamFunc getStreamFunc[2] = {do_get_channel_stream, do_get_channel_stream};
	ret = do_get_stream(getStreamFunc);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "get stream failed\n");
		goto err_do_get_stream;
	}

	ret = ImpStreamOff();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP stream off error\n");
		goto err_ImpStreamOn;
	}

	ImpUnBindModules();

	ret = encoder_exit_h264();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "encoder_exit_h264 failed\n");
		goto err_ImpStreamOn;
	}

	ImpFrameSourceExit();
	ImpSystemExit();

	CU_PASS();

	return;

err_do_get_stream:
	ImpStreamOff();
err_ImpStreamOn:
	ImpUnBindModules();
err_ImpBindModules:
	encoder_exit_h264();
err_encoder_init_h264:
	ImpFrameSourceExit();
err_ImpFrameSourceInit:
	ret = IMP_System_Exit();
	if (ret < 0)
		IMP_LOG_ERR(TAG, "IMP_System_Exit() failed\n");
err_ImpSystemInit:
	CU_FAIL();
}

static void test_framesource_jpeg_snap() /* SYS+emuFS+ENC+jpeg */
{
	int ret;

	ret = ImpSystemInit();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP system init failed\n");
		goto err_ImpSystemInit;
	}

	ret = ImpFrameSourceInit();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP ISP init failed\n");
		goto err_ImpFrameSourceInit;
	}

	ret = encoder_init_jpeg();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP Encoder init failed\n");
		goto err_encoder_init_jpeg;
	}

	ret = ImpBindModules();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP Encoder init failed\n");
		goto err_ImpBindModules;
	}

	ret = ImpStreamOn();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP stream on error\n");
		goto err_ImpStreamOn;
	}
	GetStreamFunc getStreamFunc[2] = {do_get_channel_snap, do_get_channel_snap};
	ret = do_get_stream(getStreamFunc);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "get stream failed\n");
		goto err_do_get_stream;
	}

	ret = ImpStreamOff();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "IMP stream off error\n");
		goto err_ImpStreamOn;
	}

	ImpUnBindModules();

	ret = encoder_exit_jpeg();
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "encoder_exit_jpeg failed\n");
		goto err_ImpStreamOn;
	}

	ImpFrameSourceExit();
	ImpSystemExit();

	CU_PASS();

	return;

err_do_get_stream:
	ImpStreamOff();
err_ImpStreamOn:
	ImpUnBindModules();
err_ImpBindModules:
	encoder_exit_jpeg();
err_encoder_init_jpeg:
	ImpFrameSourceExit();
err_ImpFrameSourceInit:
	ret = IMP_System_Exit();
	if (ret < 0)
		IMP_LOG_ERR(TAG, "IMP_System_Exit() failed\n");
err_ImpSystemInit:
	CU_FAIL();
}

CU_TestInfo FrameSourceTest[] = {
	{"SYS+FS+ENC+h264", test_framesource_h264_stream},
	{"SYS+FS+ENC+jpeg", test_framesource_jpeg_snap},
	CU_TEST_INFO_NULL,
};
